//
// created by zhangyan, all rights reserved.
//

#ifndef __DIYLANE_BUSINESS_SERVICE_SETUP_H
#define __DIYLANE_BUSINESS_SERVICE_SETUP_H

#include "business/arguments.hpp"
#include "business/error.hpp"
#include "business/result.hpp"
#include "modules/loggger/logger.hpp"
#include "modules/postgres.hpp"
#include "business/sqlpp_aliases.hpp"
#include "business/models/t_admin.h"
#include "business/models/t_role.h"

namespace diylane
{
namespace service
{
struct Setup : postgres::PooledWorker
{

  public:
    Setup()
    {
    }
    ~Setup()
    {
    }

    // singleton helper
    static Setup &singleton(void *uniquePtr = NULL)
    {
        static Setup *singletonPointer = NULL;
        if (uniquePtr)
            singletonPointer = (Setup *)uniquePtr;
        return *singletonPointer;
    }

  private:
    uint64_t get_one_admin(postgres::AcquiredWorker &w, BusinessModel::t_admin &t_admin)
    {
        uint64_t id_ = 0;
        for (const auto &row : w.unwrap()->conn_->run(sqlpp::select(t_admin.id_)
                                                          .from(t_admin)
                                                          .unconditionally()
                                                          .limit((uint32_t)1)))
        {
            id_ = row.id_;
        }
        return id_;
    }

    uint64_t get_super_role(postgres::AcquiredWorker &w, BusinessModel::t_role &t_role)
    {
        uint64_t id_ = 0;
        for (const auto &row : w.unwrap()->conn_->run(sqlpp::select(t_role.id_)
                                                          .from(t_role)
                                                          .where(t_role.name_ == "super")
                                                          .limit((uint32_t)1)))
        {
            id_ = row.id_;
        }
        return id_;
    }

  public:
    void createAdmin(Arguments &args, Result &result)
    {
        if (!args.exist("username"))
        {
            result.setError(DLE_Param, "Parameter <username> not specified");
            return;
        }
        if (!args.exist("password"))
        {
            result.setError(DLE_Param, "Parameter <password> not specified");
            return;
        }

        std::string username = args.get("username");
        std::string password = args.get("password");

        std::string err;
        postgres::AcquiredWorker w(pool, err);
        if (w.unwrap() == NULL)
        {
            result.setError(DLE_System, err);
            LOG_ERROR(Logger::inst()) << err;
            return;
        }

        BusinessModel::t_admin admin;
        BusinessModel::t_role role;

        try
        {
            w.unwrap()->conn_->start_transaction();

            // 已经存在管理员账号?
            if (get_one_admin(w, admin) != 0)
            {
                result.setError(DLE_Exist, "Already exist");
                throw(int) DLE_Exist;
            }

            // 创建管理员
            // 1.先检查角色"super"是否存在，如果不存在就创建一个
            // 2.创建一个super

            uint64_t roleId = get_super_role(w, role);
            if (roleId == 0)
            {
                auto stmt_insert_role =
                    sqlpp::dynamic_insert_into(*w.unwrap()->conn_, role)
                        .dynamic_set(
                            role.name_ = "super",
                            role.name_aliase_ = "超级管理员",
                            role.privilege_ = 100);
                w.unwrap()->conn_->run_(stmt_insert_role);
            }
            roleId = get_super_role(w, role);
            if (roleId == 0)
            {
                result.setError(DLE_System, "Failed to create a super role");
                throw(int) DLE_System;
            }

            auto stmt_insert_admin =
                sqlpp::dynamic_insert_into(*w.unwrap()->conn_, admin)
                    .dynamic_set(
                        admin.name_ = username,
                        admin.password_ = password,
                        admin.role_ = roleId);
            w.unwrap()->conn_->run_(stmt_insert_admin);

            w.unwrap()->conn_->commit_transaction();
        }
        catch (int errCode)
        {
            switch (errCode)
            {
            case DLE_Exist:
                break;
            default:
                result.setError(DLE_Unknown, "Unknown error code");
            }
            w.unwrap()->conn_->rollback_transaction(false);
            return;
        }
        catch (const std::exception &e)
        {
            result.setError(DLE_Unknown, e.what());
            LOG_ERROR(Logger::inst()) << e.what() << std::endl;
            w.unwrap()->conn_->rollback_transaction(false);
            return;
        }
    }
};
}
}

#endif
